package org.carabiner.infinitest;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;

class ResultTreeModel extends DefaultTreeModel implements TestStatusListener
{
  public ResultTreeModel()
  {
    super(new DefaultMutableTreeNode("Failures", true));
  }

  public void testFailed(TestEvent e)
  {
    MutableTreeNode caseNode = findNode(getRoot(), e);
    if (caseNode == null)    
      caseNode = addNode(e);
    TestTreeNode methodNode = findNode(caseNode, e);    
    if (methodNode == null)
      addMethodNode(caseNode, e);
    else
    {
      methodNode.updateNode(e);
      fireTreeNodesChanged(this, getPathToRoot(methodNode), null, new Object[]{methodNode});
    }
  }

  public void testError(TestEvent event)
  {
    testFailed(event);
  }
  
  public void testMethodSucceeded(TestEvent event)
  {
    MutableTreeNode caseNode = findNode(getRoot(), event);    
    if (caseNode != null)
    {
      MutableTreeNode methodNode = findNode(caseNode, event);
      if (methodNode != null)
        removeNodeFromParent(methodNode);
      if (caseNode.getChildCount() == 0)
        removeNodeFromParent(caseNode);
    }    
  }

  private MutableTreeNode addNode(TestEvent testEvent)
  {  
    MutableTreeNode caseNode = new TestTreeNode(testEvent);
    insertNodeInto(caseNode, getRootNode(), 0);
    return caseNode;
  }

  private void addMethodNode(MutableTreeNode caseNode, TestEvent testEvent)
  {    
    MutableTreeNode methodNode = new ResultTreeNode(testEvent);
    insertNodeInto(methodNode, caseNode, 0);    
  }

  private Object createUserObject(TestEvent e)
  {
    return e.getTestMethod();
  }
  
  private MutableTreeNode getRootNode()
  {
    return (MutableTreeNode)getRoot();
  }

  private TestTreeNode findNode(Object parentNode, TestEvent event)
  {
    for(int i = 0; i < getChildCount(parentNode); i++)
    {
      TestTreeNode node = (TestTreeNode) getChild(parentNode, i);
      if (node.isNodeForEvent(event))
        return node;
    }
    return null;
  }

  public int getTestCaseCount()
  {
    return getChildCount(getRoot());
  }

  public TreeNode getTestCase(int i)
  {
    return (TreeNode) getChild(getRoot(), i);
  }
  
  private class TestTreeNode extends DefaultMutableTreeNode implements InfinitestTreeNode
  {
    public TestTreeNode(TestEvent testEvent)
    {
      super(testEvent);
      setAllowsChildren(true);
    }
    
    public void updateNode(TestEvent e)
    {
      setUserObject(e);
    }

    public TestEvent getTestEvent()
    {
      return (TestEvent) getUserObject();
    }
    
    public boolean isNodeForEvent(TestEvent e)
    {
      return e.getTestName().equals(getTestEvent().getTestName());
    }
    
    public String toString()
    {      
      return getTestEvent().getTestName();
    }
  }
  
  private class ResultTreeNode extends TestTreeNode
  {
    public ResultTreeNode(TestEvent testEvent)
    {
      super(testEvent);
      setAllowsChildren(false);
    }
    
    public boolean isNodeForEvent(TestEvent e)
    {
      return super.isNodeForEvent(e) && 
          e.getTestMethod().equals(getTestEvent().getTestMethod());
    }
    
    public String toString()
    {
      if (getTestEvent().getError() != null)
        return getTestEvent().getTestMethod().toString() + 
               ": " +
               getTestEvent().getError().getClass().getName() + 
               "(" + 
               getTestEvent().getMessage() +
               ")";
      return getTestEvent().getTestMethod().toString() + ": " + getTestEvent().getMessage();
    }    
  }

  public void testCaseSucceeded(TestEvent event)
  {
    MutableTreeNode caseNode = findNode(getRoot(), event);
    if (caseNode != null)
      removeNodeFromParent(caseNode);
  }
}
